﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace STSdb.MTL.Utils.Bytes
{
    public static class BytesExtensions
    {

        /// <summary>
        /// Enlarges byte array till given size filling with 0 from start the rest of the length.
        /// Ex: byte[] a = new byte[] {1,2,3}; a.EnlargeByteArray_BigEndian(6) = new byte[] {0,0,0,1,2,3};
        /// If array for enlargement equals null new byte[size] will be returned, if array for enlargement length more or equal size then the same array will be returned.
        /// </summary>
        /// <param name="ar"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public static byte[] EnlargeByteArray_BigEndian(this byte[] ar, int size)
        {
            if (ar == null)
                return new byte[size];

            if (ar.Length >= size)
                return ar;

            byte[] rb = new byte[size];
            Buffer.BlockCopy(ar, 0, rb, size - ar.Length, ar.Length);
            return rb;
        }

        /// <summary>
        /// Substring int-dimensional byte arrays
        /// </summary>
        /// <param name="ar"></param>
        /// <param name="startIndex"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static byte[] Substring(this byte[] ar, int startIndex, int length)
        {
            return substringByteArray(ar, startIndex, length);
        }

        /// <summary>
        /// Substring int-dimensional byte arrays from and till the end
        /// </summary>
        /// <param name="ar"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static byte[] Substring(this byte[] ar, int startIndex)
        {
            if (ar == null)
                return null;

            return substringByteArray(ar, startIndex, ar.Length);
        }

        /// <summary>
        /// Works only for int-dimesional arrays only
        /// </summary>
        /// <param name="ar"></param>
        /// <returns></returns>
        public static byte[] CloneArray(this byte[] ar)
        {
            byte[] rb = null;

            if (ar == null)
                return null;

            if (ar.Length < 1)
                return new byte[] { };

            rb = new byte[ar.Length];

            Buffer.BlockCopy(ar, 0, rb, 0, ar.Length);

            return rb;
        }


        #region "Bytes concatenation"
        /// <summary>
        /// Fastest Method. Works only for int-dimesional arrays only. 
        /// When necessary to concat many arrays use ConcatMany
        /// </summary>
        /// <param name="ar1"></param>
        /// <param name="ar2"></param>
        /// <returns></returns>
        public static byte[] Concat(this byte[] ar1, byte[] ar2)
        {
            if (ar1 == null)
                ar1 = new byte[] { };
            if (ar2 == null)
                ar2 = new byte[] { };

            byte[] ret = null;

            ret = new byte[ar1.Length + ar2.Length];

            Buffer.BlockCopy(ar1, 0, ret, 0, ar1.Length);
            Buffer.BlockCopy(ar2, 0, ret, ar1.Length, ar2.Length);

            return ret;
        }

        public static byte[] Concat(this byte ar1, byte ar2)
        {
            return (new byte[] { ar1 }).Concat(new byte[] { ar2 });
        }

        public static byte[] Concat(this byte ar1, byte[] ar2)
        {
            return (new byte[] { ar1 }).Concat(ar2);
        }

        public static byte[] Concat(this byte[] ar1, byte ar2)
        {
            return ar1.Concat(new byte[] { ar2 });
        }

        ///// <summary>
        ///// Fastest Method (the same as Concat). Works only for int-dimesional arrays only
        ///// </summary>
        ///// <param name="ar1"></param>
        ///// <param name="ar2"></param>
        ///// <returns></returns>
        //public static byte[] _ConcatByteArray(this byte[] ar1, byte[] ar2)
        //{
        //    if (ar1 == null)
        //        ar1 = new byte[] { };
        //    if (ar2 == null)
        //        ar2 = new byte[] { };
        //    byte[] ret = new byte[ar1.Length + ar2.Length];

        //    Buffer.BlockCopy(ar1, 0, ret, 0, ar1.Length);
        //    Buffer.BlockCopy(ar2, 0, ret, ar1.Length, ar2.Length);

        //    return ret;
        //}

        /// <summary>
        /// Fast when necessary to concat many arrays
        /// Example: byte[] s = new byte[] { 1, 2, 3 }; s.ConcatMany(new byte[] { 2, 3, 4 }, new byte[] { 3, 7, 8 }, new byte[] { 9, 10, 11 });
        /// Also: ((byte[])null).ConcatMany(new byte[] { 2, 3, 4 }, new byte[] { 3, 7, 8 }, new byte[] { 9, 10, 11 });
        /// </summary>
        /// <param name="ar1"></param>
        /// <param name="ar2"></param>
        /// <returns></returns>
        public static byte[] ConcatMany(this byte[] ar1, params byte[][] arrays)
        {
            if (ar1 == null)
                ar1 = new byte[] { };

            byte[] ret = new byte[ar1.Length + arrays.Sum(x => (x == null) ? 0 : x.Length)];
            int offset = 0;

            Buffer.BlockCopy(ar1, 0, ret, offset, ar1.Length);
            offset += ar1.Length;

            foreach (byte[] data in arrays)
            {
                if (data == null) //faster than foreach (byte[] data in arrays.Where(r=>r != null))
                    continue;

                Buffer.BlockCopy(data, 0, ret, offset, data.Length);
                offset += data.Length;
            }
            return ret;

        }


        ///// <summary>
        ///// Slower then _ConcatByteArray. Linq concat is used. Yield return is used. Be sure byte arrays are safely boxed. ToArray() is used to get byte[] from IEnumerable.
        ///// Can have less memory consumption in compare with _ConcatByteArray before GC.
        ///// Example: byte[] s = new byte[] { 1, 2, 3 }; s._LinqConcatB(new byte[] { 2, 3, 4 }, new byte[] { 3, 7, 8 });
        ///// </summary>
        ///// <param name="ar1"></param>
        ///// <param name="arrays"></param>
        ///// <returns></returns>
        //public static byte[] _LinqConcatByteArrays(this byte[] ar1, params byte[][] arrays)
        //{
        //    if(ar1 == null)
        //        ar1 = new byte[] { };
        //    return ar1.Concat(_LinqConcatByteArraysIE(arrays).ToArray());
        //}

        ///// <summary>
        ///// Slower then _ConcatByteArray. Linq concat is used. Yield return is used. Be sure byte arrays are safely boxed. ToArray() is used to get byte[] from IEnumerable.
        ///// Can have less memory consumption in compare with _ConcatByteArray before GC.
        ///// Example: _LinqConcatB1(new byte[] { 2, 3, 4 }, new byte[] { 3, 7, 8 });
        ///// 
        ///// </summary>
        ///// <param name="arrays"></param>
        ///// <returns></returns>
        //public static byte[] _LinqConcatByteArrays(params byte[][] arrays)
        //{
        //    return _LinqConcatByteArraysIE(arrays).ToArray();
        //}

        ///// <summary>
        ///// Same speed as _ConcatByteArray, but returns not byte[]. Converting can take from 10% of time. Cant't be used in recursion before converting to byte[]. Linq concat is used. Yield return is used. result will be IEnumerable of byte
        ///// Can have less memory consumption in compare with _ConcatByteArray before GC.
        /////  _LinqConcatIE(new byte[] { 2, 3, 4 }, new byte[] { 3, 7, 8 });
        ///// </summary>
        ///// <param name="arrays"></param>
        ///// <returns></returns>
        //public static IEnumerable<byte> _LinqConcatByteArraysIE(params byte[][] arrays)
        //{
        //    foreach (byte[] a in arrays)
        //    {
        //        if (a == null)
        //            continue;
        //        foreach (byte b in a)
        //            yield return b;
        //    }
        //}
        #endregion


        private static byte[] substringByteArray(byte[] ar, int startIndex, int length)
        {
            if (ar == null)
                return null;

            if (ar.Length <= 0)
                return ar;

            if (startIndex > ar.Length - 1)
                return null;

            if (startIndex + length > ar.Length)
            {
                //we make length till the end of array
                length = ar.Length - startIndex;
            }

            byte[] ret = new byte[length];


            Buffer.BlockCopy(ar, startIndex, ret, 0, length);

            return ret;
        }



        /// <summary>
        /// If not found returns -1
        /// </summary>
        /// <param name="ar"></param>
        /// <param name="search"></param>
        /// <param name="en"></param>
        /// <returns></returns>
        public static int indexOfStringInByteArray(this byte[] ar, string search, Encoding en)
        {
            if (ar.Length < search.Length)
                return -1;

            byte[] sr = en.GetBytes(search);
            int returnIndex = -1;

            for (int i = 0; i < ar.Length - search.Length + 1; i++)
            {
                for (int j = 0; j < sr.Length; j++)
                {
                    if (ar[i + j] != sr[j])
                    {
                        returnIndex = -1;
                        break;
                    }
                    returnIndex++;
                }

                if (returnIndex != -1)
                    return i;
            }

            return -1;
        }

        /// <summary>
        /// If both arrays are null, returns true. Checks nulls also. Uses SequenceEqual
        /// </summary>
        /// <param name="ar"></param>
        /// <param name="compareArray"></param>
        /// <returns></returns>
        public static bool _Equals(this byte[] ar, byte[] compareArray)
        {
            if (ar == null && compareArray == null)
                return true;

            if (ar == null)
                return false;

            if (compareArray == null)
                return false;

            if (ar.Length != compareArray.Length)
                return false;

            return ar.SequenceEqual(compareArray);
        }

        /// <summary>
        /// Managed way compares 2 bytes array. Uses for loop and extra checks like null, length before
        /// </summary>
        /// <param name="b1"></param>
        /// <param name="b2"></param>
        /// <returns></returns>
        public static bool _ByteArrayEquals(this byte[] b1, byte[] b2)
        {
            if (b1 == b2) return true;
            if (b1 == null || b2 == null) return false;
            if (b1.Length != b2.Length) return false;
            for (int i = 0; i < b1.Length; i++)
            {
                if (b1[i] != b2[i]) return false;
            }
            return true;

            ///////////////////////////////////////////////////////////unmanaged way
            //if (b1 == b2) return true;
            //if (b1 == null || b2 == null) return false;
            //unsafe
            //{
            //    if (b1.Length != b2.Length)
            //        return false;

            //    int n = b1.Length;

            //    fixed (byte* p1 = b1, p2 = b2)
            //    {
            //        byte* ptr1 = p1;
            //        byte* ptr2 = p2;

            //        while (n-- > 0)
            //        {
            //            if (*ptr1++ != *ptr2++)
            //                return false;
            //        }
            //    }

            //    return true;
            //}
            ////////////////////////////////////////////////////////////////////////

        }



        //0x0801 
        //Big Endian - First comes higer: 0x08 0x01 = 2049
        //Little Endian - First comes lower 0x01 0x08 = 2049



        #region "Conversions Bytes To Other"

        #region "Int16"
        /// <summary>
        /// From 2 bytes array which is in BigEndian order (highest byte first, lowest last) makes short.
        /// If array not equal 2 bytes throws exception. (-32,768 to 32,767)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static short To_Int16_BigEndian(this byte[] value)
        {
            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.ToInt16(value, 0);
            }
            else
            {
                return BitConverter.ToInt16(value.Reverse().ToArray(), 0);
            }
        }

        /// <summary>
        /// From 2 bytes array which is in LittleEndian order (lowest byte first, highest last) makes short.
        /// If array not equal 2 bytes throws exception. (-32,768 to 32,767)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static short To_Int16_LittleEndian(this byte[] value)
        {
            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.ToInt16(value.Reverse().ToArray(), 0);
            }
            else
            {
                return BitConverter.ToInt16(value, 0);
            }
        }
        #endregion

        #region "UInt16"
        /// <summary>
        /// From 2 bytes array which is in BigEndian order (highest byte first, lowest last) makes ushort.
        /// If array not equal 2 bytes throws exception. (0 to 65,535)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static ushort To_UInt16_BigEndian(this byte[] value)
        {
            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.ToUInt16(value, 0);
            }
            else
            {
                return BitConverter.ToUInt16(value.Reverse().ToArray(), 0);
            }
        }

        /// <summary>
        /// From 2 bytes array which is in LittleEndian order (lowest byte first, highest last) makes ushort.
        /// If array not equal 2 bytes throws exception. (0 to 65,535)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static ushort To_UInt16_LittleEndian(this byte[] value)
        {
            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.ToUInt16(value.Reverse().ToArray(), 0);
            }
            else
            {
                return BitConverter.ToUInt16(value, 0);
            }
        }
        #endregion

        #region "Int32"
        /// <summary>
        /// From 4 bytes array which is in BigEndian order (highest byte first, lowest last) makes int.
        /// If array not equal 4 bytes throws exception. (-2,147,483,648 to 2,147,483,647)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static int To_Int32_BigEndian(this byte[] value)
        {
            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.ToInt32(value, 0);
            }
            else
            {
                return BitConverter.ToInt32(value.Reverse().ToArray(), 0);
            }
        }

        /// <summary>
        /// From 4 bytes array which is in LittleEndian order (lowest byte first, highest last) makes int.
        /// If array not equal 4 bytes throws exception. (-2,147,483,648 to 2,147,483,647)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static int To_Int32_LittleEndian(this byte[] value)
        {
            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.ToInt32(value.Reverse().ToArray(), 0);
            }
            else
            {
                return BitConverter.ToInt32(value, 0);
            }
        }
        #endregion

        #region "UInt32"
        /// <summary>
        /// From 4 bytes array which is in BigEndian order (highest byte first, lowest last) makes uint.
        /// If array not equal 4 bytes throws exception. (0 to 4.294.967.295)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static uint To_UInt32_BigEndian(this byte[] value)
        {
            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.ToUInt32(value, 0);
            }
            else
            {
                return BitConverter.ToUInt32(value.Reverse().ToArray(), 0);
            }
        }

        /// <summary>
        /// From 4 bytes array which is in LittleEndian order (lowest byte first, highest last) makes uint.
        /// If array not equal 4 bytes throws exception. (0 to 4.294.967.295)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static uint To_UInt32_LittleEndian(this byte[] value)
        {
            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.ToUInt32(value.Reverse().ToArray(), 0);
            }
            else
            {
                return BitConverter.ToUInt32(value, 0);
            }
        }
        #endregion

        #region "Int64"
        /// <summary>
        /// From 8 bytes array which is in BigEndian order (highest byte first, lowest last) makes long.
        /// If array not equal 8 bytes throws exception. (-9.223.372.036.854.775.808 bis 9.223.372.036.854.775.807)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static long To_Int64_BigEndian(this byte[] value)
        {
            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.ToInt64(value, 0);
            }
            else
            {
                return BitConverter.ToInt64(value.Reverse().ToArray(), 0);
            }
        }

        /// <summary>
        /// From 8 bytes array which is in LittleEndian order (lowest byte first, highest last) makes long.
        /// If array not equal 8 bytes throws exception. (-9.223.372.036.854.775.808 bis 9.223.372.036.854.775.807)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static long To_Int64_LittleEndian(this byte[] value)
        {
            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.ToInt64(value.Reverse().ToArray(), 0);
            }
            else
            {
                return BitConverter.ToInt64(value, 0);
            }
        }
        #endregion

        #region "UInt64"
        /// <summary>
        /// From 8 bytes array which is in BigEndian order (highest byte first, lowest last) makes ulong.
        /// If array not equal 8 bytes throws exception. (0 to 18,446,744,073,709,551,615)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static ulong To_UInt64_BigEndian(this byte[] value)
        {
            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.ToUInt64(value, 0);
            }
            else
            {
                return BitConverter.ToUInt64(value.Reverse().ToArray(), 0);
            }
        }

        /// <summary>
        /// From 8 bytes array which is in LittleEndian order (lowest byte first, highest last) makes ulong.
        /// If array not equal 8 bytes throws exception. (0 to 18,446,744,073,709,551,615)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static ulong To_UInt64_LittleEndian(this byte[] value)
        {
            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.ToUInt64(value.Reverse().ToArray(), 0);
            }
            else
            {
                return BitConverter.ToUInt64(value, 0);
            }
        }
        #endregion

        #endregion

        #region "Conversions Other to Bytes"

        #region "Int16"
        /// <summary>
        /// From Int16 to 2 bytes array with BigEndian order (highest byte first, lowest last).        
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] To_2_bytes_array_BigEndian(this short value)
        {

            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.GetBytes(value);
            }
            else
            {
                return BitConverter.GetBytes(value).Reverse().ToArray();
            }
        }

        /// <summary>
        /// From Int16 to 2 bytes array with LittleEndian order (lowest byte first, highest last).        
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] To_2_bytes_array_LittleEndian(this short value)
        {

            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.GetBytes(value).Reverse().ToArray();

            }
            else
            {
                return BitConverter.GetBytes(value);
            }
        }
        #endregion

        #region "UInt16"
        /// <summary>
        /// From UInt16 to 2 bytes array with BigEndian order (highest byte first, lowest last).        
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] To_2_bytes_array_BigEndian(this ushort value)
        {

            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.GetBytes(value);
            }
            else
            {
                return BitConverter.GetBytes(value).Reverse().ToArray();
            }
        }

        /// <summary>
        /// From UInt16 to 2 bytes array with LittleEndian order (lowest byte first, highest last).        
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] To_2_bytes_array_LittleEndian(this ushort value)
        {

            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.GetBytes(value).Reverse().ToArray();

            }
            else
            {
                return BitConverter.GetBytes(value);
            }
        }
        #endregion

        #region "Int32"
        /// <summary>
        /// From Int32 to 4 bytes array with BigEndian order (highest byte first, lowest last).        
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] To_4_bytes_array_BigEndian(this int value)
        {

            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.GetBytes(value);
            }
            else
            {
                return BitConverter.GetBytes(value).Reverse().ToArray();
            }
        }

        /// <summary>
        /// From Int32 to 4 bytes array with LittleEndian order (lowest byte first, highest last).        
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] To_4_bytes_array_LittleEndian(this int value)
        {

            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.GetBytes(value).Reverse().ToArray();

            }
            else
            {
                return BitConverter.GetBytes(value);
            }
        }
        #endregion

        #region "UInt32"
        /// <summary>
        /// From UInt32 to 4 bytes array with BigEndian order (highest byte first, lowest last).        
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] To_4_bytes_array_BigEndian(this uint value)
        {

            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.GetBytes(value);
            }
            else
            {
                return BitConverter.GetBytes(value).Reverse().ToArray();
            }
        }

        /// <summary>
        /// From UInt32 to 4 bytes array with LittleEndian order (lowest byte first, highest last).        
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] To_4_bytes_array_LittleEndian(this uint value)
        {

            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.GetBytes(value).Reverse().ToArray();

            }
            else
            {
                return BitConverter.GetBytes(value);
            }
        }
        #endregion

        #region "Int64"
        /// <summary>
        /// From Int64 to 8 bytes array with BigEndian order (highest byte first, lowest last).        
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] To_8_bytes_array_BigEndian(this long value)
        {

            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.GetBytes(value);
            }
            else
            {
                return BitConverter.GetBytes(value).Reverse().ToArray();
            }
        }

        /// <summary>
        /// From Int64 to 8 bytes array with LittleEndian order (lowest byte first, highest last).        
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] To_8_bytes_array_LittleEndian(this long value)
        {

            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.GetBytes(value).Reverse().ToArray();

            }
            else
            {
                return BitConverter.GetBytes(value);
            }
        }
        #endregion

        #region "UInt64"
        /// <summary>
        /// From UInt64 to 8 bytes array with BigEndian order (highest byte first, lowest last).        
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] To_8_bytes_array_BigEndian(this ulong value)
        {

            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.GetBytes(value);
            }
            else
            {
                return BitConverter.GetBytes(value).Reverse().ToArray();
            }
        }

        /// <summary>
        /// From UInt64 to 8 bytes array with LittleEndian order (lowest byte first, highest last).        
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] To_8_bytes_array_LittleEndian(this ulong value)
        {

            if (!BitConverter.IsLittleEndian)
            {
                return BitConverter.GetBytes(value).Reverse().ToArray();

            }
            else
            {
                return BitConverter.GetBytes(value);
            }
        }
        #endregion

        #endregion


        #region "Bytes To String"

        /// <summary>
        /// Converts BytesArray to String Representation: 00-00-00-00-1F-00-00-00-00-20.
        /// If array is null or 0 length - returns String.Empty.
        /// If replaceWith is String.Empty returns such view 00-00-00-00-1F-00-00-00-00-20.
        /// Otherwise takes such view (-00-00-00-00-1F-00-00-00-00-20) and replaces "-" with replaceWith also calls Trim()
        /// </summary>
        /// <param name="dBytes"></param>
        /// <param name="replaceWith"></param>
        /// <returns></returns>
        public static string ToBytesString(this byte[] dBytes, string replaceWith)
        {
            if (dBytes == null)
                return String.Empty;
            if (dBytes.Count() == 0)
                return String.Empty;

            if (replaceWith == String.Empty)
                return BitConverter.ToString(dBytes);

            return ("-" + BitConverter.ToString(dBytes)).Replace("-", replaceWith).Trim();
        }

        public static string ToAsciiString(this byte[] dBytes)
        {
            return (dBytes == null) ? String.Empty : System.Text.Encoding.ASCII.GetString(dBytes);
        }

        public static string ToUTF8String(this byte[] dBytes)
        {
            return (dBytes == null) ? String.Empty : System.Text.Encoding.UTF8.GetString(dBytes);
        }

        public static string ToUnicodeString(this byte[] dBytes)
        {
            return (dBytes == null) ? String.Empty : System.Text.Encoding.Unicode.GetString(dBytes);
        }

        #endregion


        #region "CRC16"

        /// <summary>
        /// Returns byte representation of Crc16
        /// </summary>
        /// <param name="ar"></param>
        /// <returns></returns>
        public static byte[] Get_CRC16_AsByteArray(this byte[] ar)
        {
            return Crc16.ComputeChecksumBytes(ar);
        }


        ///// <summary>
        ///// Returns ushort representation of Crc16
        ///// </summary>
        ///// <param name="ar"></param>
        ///// <returns></returns>
        //public static ushort ToCrc16ushort(this byte[] ar)
        //{
        //    return Crc16.ComputeChecksum(ar);
        //}

        private static class Crc16
        {
            const ushort polynomial = 0xA001;
            private static bool IsInitialized = false;
            private static ushort[] table = new ushort[256];
            private static object lock_IsInitialized = new object();

            public static ushort ComputeChecksum(byte[] bytes)
            {
                if (!Crc16.IsInitialized)
                {
                    lock (lock_IsInitialized)
                    {
                        if (!Crc16.IsInitialized)
                            Crc16.InitializeMe();
                    }
                }

                ushort crc = 0;
                for (int i = 0; i < bytes.Length; ++i)
                {
                    byte index = (byte)(crc ^ bytes[i]);
                    crc = (ushort)((crc >> 8) ^ Crc16.table[index]);
                }
                return crc;
            }

            public static byte[] ComputeChecksumBytes(byte[] bytes)
            {
                ushort crc = ComputeChecksum(bytes);
                return new byte[] { (byte)(crc >> 8), (byte)(crc & 0x00ff) };
            }


            private static void InitializeMe()
            {
                ushort value;
                ushort temp;
                for (ushort i = 0; i < table.Length; ++i)
                {
                    value = 0;
                    temp = i;
                    for (byte j = 0; j < 8; ++j)
                    {
                        if (((value ^ temp) & 0x0001) != 0)
                        {
                            value = (ushort)((value >> 1) ^ polynomial);
                        }
                        else
                        {
                            value >>= 1;
                        }
                        temp >>= 1;
                    }
                    Crc16.table[i] = value;
                }


                Crc16.IsInitialized = true;
            }
        }

        #endregion
    }
}
